#include "../headers/Camera.h"
#include "../headers/Base.h"
#include "../tools/Util.h"
#include "../headers/Game.h"

CCamera::CCamera(int nWidth, int nHeight) : 
m_nViewPortHeight(nHeight),
m_nViewPortWidth(nWidth),
m_pFollowingObject(NULL),
m_bFollowingObject(false),
m_bMovingAlongPath(false),
m_fTimer(0.0f),
m_fTravelTime(0.0f),
m_CurrentPath(NULL)
{
	m_nHalfWidth = m_nViewPortWidth >> 1;
	m_nHalfHeight = m_nViewPortHeight >> 1;
	CEventSystem::GetInstance()->RegisterClient("rezchanged", this);
}

CCamera::~CCamera()
{
	CEventSystem::GetInstance()->UnregisterClient("rezchanged", this);
}

//Moves the camera from one point to the other over a period of time
void CCamera::MoveAlongPath(vector2D vStartPosition, vector2D vEndPosition, float fTimeToTravel)
{
	m_vPathPositions.clear();
	m_vPosition = vStartPosition;
	m_CurrentPath = NULL;

	if(m_vPosition.fX < m_rBounds.left)
		m_vPosition.fX = (float)m_rBounds.left;
	if(m_vPosition.fX + (float)(m_nHalfWidth << 1) > m_rBounds.right)
		m_vPosition.fX = m_rBounds.right - (float)(m_nHalfWidth << 1);
	if(m_vPosition.fY < m_rBounds.top)
		m_vPosition.fY = (float)m_rBounds.top;
	if(m_vPosition.fY + (float)(m_nHalfHeight << 1) > m_rBounds.bottom)
		m_vPosition.fY = m_rBounds.bottom - (float)(m_nHalfHeight << 1);


	if(vEndPosition.fX < m_rBounds.left)
		vEndPosition.fX = (float)m_rBounds.left;
	if(vEndPosition.fX + (float)(m_nHalfWidth << 1) > m_rBounds.right)
		vEndPosition.fX = m_rBounds.right - (float)(m_nHalfWidth << 1);
	if(vEndPosition.fY < m_rBounds.top)
		vEndPosition.fY = (float)m_rBounds.top;
	if(vEndPosition.fY + (float)(m_nHalfHeight << 1) > m_rBounds.bottom)
		vEndPosition.fY = m_rBounds.bottom - (float)(m_nHalfHeight << 1);

	AddPointAlongPath(vEndPosition, fTimeToTravel);



}

//Move the camera along a series of positions over given time periods
void CCamera::MoveAlongPath(vector<vector2D> vPositions, vector<float> vTimes)
{
	m_vPathPositions.clear();
	m_vPosition = vPositions[0];
	m_CurrentPath = NULL;

	if(vPositions.empty())
	{
		COUT("Positions empty in MoveAlongPath");
		return;
	}
	if(vPositions.size() != vTimes.size())
	{
		COUT("Positions and times to do match in MoveAlongPath");
		return;
	}
	for(vector<vector2D>::iterator i = vPositions.begin(); i != vPositions.end(); ++i)
	{
		if(i->fX < m_rBounds.left)
			i->fX = (float)m_rBounds.left;
		if(i->fX + (float)(m_nHalfWidth << 1) > m_rBounds.right)
			i->fX = m_rBounds.right - (float)(m_nHalfWidth << 1);
		if(i->fY < m_rBounds.top)
			i->fY = (float)m_rBounds.top;
		if(i->fY + (float)(m_nHalfHeight << 1) > m_rBounds.bottom)
			i->fY = m_rBounds.bottom - (float)(m_nHalfHeight << 1);
	}

	int size = vPositions.size();

	for(int i = 0; i < size; i++)
	{
		AddPointAlongPath(vPositions[i], vTimes[i]);
	}
}

//Add a position for the camera to go to
void CCamera::AddPointAlongPath(vector2D vDestPosition, float fTime)
{
	m_bMovingAlongPath = true;
	m_bFollowingObject = false;

	PositionTimePair ptp;
	if(m_vPathPositions.empty())
	{		
		ptp.m_vPosition1 = m_vPosition;
	}
	else
	{
		ptp.m_vPosition1 = m_vPathPositions.back().m_vPosition2;
	}
	ptp.m_vPosition2 = vDestPosition;
	ptp.m_fTime = fTime;
	m_vPathPositions.push_back(ptp);
}


void CCamera::Update(float dt)
{
	if(m_bFollowingObject && m_pFollowingObject != NULL)
	{
		m_vPosition =  m_pFollowingObject->GetPosition() - vector2D((float)m_nHalfWidth, (float)m_nHalfHeight);
		if(m_vPosition.fX < m_rBounds.left)
			m_vPosition.fX = (float)m_rBounds.left;
		if(m_vPosition.fX + (float)(m_nHalfWidth << 1) > m_rBounds.right)
			m_vPosition.fX = m_rBounds.right - (float)(m_nHalfWidth << 1);
		if(m_vPosition.fY < m_rBounds.top)
			m_vPosition.fY = (float)m_rBounds.top;
		if(m_vPosition.fY + (float)(m_nHalfHeight << 1) > m_rBounds.bottom)
			m_vPosition.fY = m_rBounds.bottom - (float)(m_nHalfHeight << 1);
	}
	else if(m_bMovingAlongPath)
	{
		if(m_CurrentPath == NULL)
		{
			if(m_vPathPositions.empty() == false)
			{
				m_CurrentPath = &m_vPathPositions.front();
				m_vPosition = m_CurrentPath->m_vPosition1;
				m_fTimer = 0.0f;
				m_fTravelTime = m_CurrentPath->m_fTime;
			}
		}
		else
		{
			m_fTimer += dt;
			if(m_fTimer >= m_fTravelTime)
			{
				m_vPathPositions.pop_front();
				if(m_vPathPositions.empty() == false)
				{
					m_CurrentPath = &m_vPathPositions.front();
					m_vPosition = m_CurrentPath->m_vPosition1;
					m_fTimer = 0.0f;
					m_fTravelTime = m_CurrentPath->m_fTime;
				}
				else
					m_CurrentPath = false;
			}
			if(m_CurrentPath != NULL)
				m_vPosition = Lerp<vector2D>(m_CurrentPath->m_vPosition1, m_CurrentPath->m_vPosition2, m_fTimer / m_fTravelTime);
		}
	}
}

void CCamera::StopPath()
{
	m_bMovingAlongPath = false;
	m_vPathPositions.clear();
}

void CCamera::AttachToObject(CBase * pObject)
{
	m_pFollowingObject = pObject;
	m_bFollowingObject = true;
	m_bMovingAlongPath = false;
}

void CCamera::FreeFromObject()
{
	m_bFollowingObject = false;
	m_pFollowingObject = NULL;
}

bool CCamera::HandleEvent(CEvent * pEvent)
{
	if(pEvent->GetEventID() == "rezchanged")
	{
		m_nViewPortWidth = CGame::GetInstance()->GetScreenWidth();
		m_nViewPortHeight = CGame::GetInstance()->GetScreenHeight();
		m_nHalfWidth = m_nViewPortWidth >> 1;
		m_nHalfHeight = m_nViewPortHeight >> 1;
	}

	return true;
}